package cn._2dland.uploader.xina;

import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.json.JSONArray;
import org.json.JSONObject;

import cn._2dland.uploader.CountingMultipartRequestEntity;
import cn._2dland.uploader.HttpException;
import cn._2dland.uploader.UploadClient;
import cn._2dland.uploader.UploadHandler;
import cn._2dland.uploader.UploadTask;
import cn._2dland.uploader.utils.HttpUtils;
import cn._2dland.uploader.utils.Logger;

public class XinaClient extends UploadClient {
	/** 预登录API */
	private static final String PRE_LOGIN_URL = "http://login.sina.com.cn/sso/prelogin.php";
	/** 登录API */
	private static final String LOGIN_URL = "http://login.sina.com.cn/sso/login.php?client=sinaSSOManager.js(v1.2.3)";
	/** 上传API */
	private static final String UPLOAD_URL = "http://vupload.you.video.sina.com.cn/cgi-bin/post.cgi";
	/** 提交信息API */
	private static final String POST_URL = "http://vupload.you.video.sina.com.cn/upload_com.php";
	/** 获取hach值的URL */
	private static final String HACH_URL = "http://vupload.you.video.sina.com.cn/u.php?m=2";

	public XinaClient(String user, String pass) {
		super(user, pass);
	}

	public boolean login() {
		PostMethod loginPost = new PostMethod(LOGIN_URL);
		try {
			Logger.writec("登录账户 " + username + " ...");

			// 先发送预登录请求，获取相关参数
			Map<String, String> params = getLoginParam();
			String serverTime = params.get("servertime");
			String nonce = params.get("nonce");
			String publicKey = params.get("pubkey");

			// 填充提交参数
			loginPost.addParameter("entry", "vblog");
			loginPost.addParameter("service", "vblog");
			loginPost.addParameter("encoding", "UTF-8");
			loginPost.addParameter("client", "sinaSSOManager.js(v1.2.3)");
			loginPost.addParameter("callback", "parent.sinaSSOManager.lb");
			loginPost.addParameter("returntype", "IFRAME");
			loginPost.addParameter("savestate", "30");
			loginPost.addParameter("useticket", "0");
			loginPost.addParameter("setdomain", "1");
			loginPost.addParameter("gateway", "1");
			loginPost.addParameter("vsnf", "1");
			loginPost.addParameter("pwencode", "rsa2");
			loginPost.addParameter("rsakv", params.get("rsakv"));
			loginPost.addParameter("su", XinaUtils.encodeUsername(username));
			loginPost.addParameter("sp", XinaUtils.encodePassword(password, publicKey, serverTime, nonce));

			// 设置引用页
			loginPost.addRequestHeader("Referer", "http://video.sina.com.cn/index.shtml");

			// 发送登录请求
			int respCode = client.executeMethod(loginPost);
			if(respCode != HttpStatus.SC_OK)
				throw new HttpException(respCode);

			String body = HttpUtils.readResponseStream(loginPost);
			// 释放资源
			loginPost.releaseConnection();
			loginPost = null;

			// 截取出json部分
			JSONObject result = parseLoginResult(body);
			bLogin = (result != null && result.getInt("retcode") == 0);

			// 执行登录的后续操作
			doAfterLogin(result);

			// 输出信息
			Logger.writec(bLogin ? "登录成功！" : "登录失败...");
		} catch (Exception e) {
			Logger.writec("登录时发生异常，请查看错误日志文件...");
			Logger.error(e);
		} finally {
			if(loginPost != null) {
				loginPost.releaseConnection();
			}
		}
		return bLogin;
	}

	public boolean upload(UploadTask task) {
		boolean result = false;
		try {
			// 获取hach值
			Logger.writec("获取上传参数...");
			String hach = getHach();
			Logger.debug("hach=" + hach);

			// 启动监视线程
			UploadLock lock = new UploadLock();
			HandleThread ht = new HandleThread(lock, client, hach);
			ht.start();

			// 构造上传请求
			StringBuffer buf = new StringBuffer(UPLOAD_URL).append("?").append(hach);
			PostMethod uploadPost = new PostMethod(buf.toString());
			Part[] parts = { new FilePart(task.fileName, task.file) };
			uploadPost.setRequestEntity(new CountingMultipartRequestEntity(parts, 
					client.getParams(), task.file.length(), new UploadHandler()));

			// 上传视频文件
			Logger.writec("开始上传文件：" + task.fileName);
			int respCode = client.executeMethod(uploadPost);
			uploadPost.releaseConnection();

			// 等待监视线程完成
			synchronized (lock) {
				while(!lock.isDone()){
					lock.wait();
				}
			}

			// 提交视频信息
			Logger.writec("文件上传完成，开始提交信息...");
			PostMethod infoPost = new PostMethod(POST_URL);
			infoPost.getParams().setContentCharset("GB2312");
			infoPost.addParameters(new NameValuePair[]{
					new NameValuePair("m", "2"),
					new NameValuePair("src_type", "400"),
					new NameValuePair("url", ""),
					new NameValuePair("attRetName", lock.getResult()),
					new NameValuePair("title", task.title),
					new NameValuePair("tags", task.tags),
					new NameValuePair("channel", task.category),
					new NameValuePair("froms", task.source),
					new NameValuePair("description", task.descript)
			});
			respCode = client.executeMethod(infoPost);
			if(respCode != HttpStatus.SC_OK) 
				throw new HttpException(respCode);

			// 获取VID
			String vid = pickVID(infoPost.getResponseBodyAsString());
			infoPost.releaseConnection();

			result = (vid != null);
			if(result) {
				Logger.writec("视频上传成功！");
				Logger.writecf(task.fileName + " -> VID=" + vid);
			} else {
				Logger.writec("视频上传失败！");
			}
			
		} catch (Exception e) {
			Logger.writec("上传时发生异常，请查看错误日志文件...");
			Logger.error(e);
		}
		return result;
	}

	/**
	 * 获取登录需要的信息（servertime和nonce）
	 * 
	 * @return
	 */
	private Map<String, String> getLoginParam() throws Exception {
		Map<String, String> map = new HashMap<String, String>();

		GetMethod paramGet = null;
		try {
			// 构造URL
			StringBuffer url = new StringBuffer(PRE_LOGIN_URL);
			url.append("?entry=").append("vblog")
				.append("&callback=").append("sinaSSOManager.preloginCallBack")
				.append("&su=").append(URLEncoder.encode(XinaUtils.encodeUsername(username), "utf-8"))
				.append("&rsakt=").append("mod");
			paramGet = new GetMethod(url.toString());

			// 发送请求
			int respCode = client.executeMethod(paramGet);
			if(respCode != HttpStatus.SC_OK)
				throw new HttpException(respCode);

			// 获取返回结果
			String body = HttpUtils.readResponseStream(paramGet).trim();
			body = body.substring(32, body.length() - 1);
			
			// 解析返回结果
			JSONObject jsObj = new JSONObject(body);
			if(jsObj.getInt("retcode") == 0) {
				// 从json中拿到对应的参数
				map.put("servertime", jsObj.getString("servertime"));
				map.put("nonce", jsObj.getString("nonce"));
				map.put("pcid", jsObj.getString("pcid"));
				map.put("pubkey", jsObj.getString("pubkey"));
				map.put("rsakv", jsObj.getString("rsakv"));
			}
		} finally {
			if(paramGet != null)
				paramGet.releaseConnection();
		}
		
		return map;
	}

	/**
	 * 从登录请求的响应文本中，获取登录结果信息
	 * 
	 * @param body
	 * @return
	 */
	private JSONObject parseLoginResult(String body) {
		JSONObject obj = null;
		try {
			int pos = body.indexOf("setCrossDomainUrlList(");
			if(pos >= 0) {
				body = body.substring(pos + 22, body.indexOf("});") + 1);
				obj = new JSONObject(body);
			}
		} catch (Exception e) {
			obj = null;
		}

		return obj;
	}

	/**
	 * 执行登录的后续操作，防止被渣浪ban IP。本操作无视任何异常，因为不会产生什么影响。
	 * 
	 * @param obj 登录的结果信息
	 */
	private void doAfterLogin(JSONObject obj) {
		if(obj == null)
			return;

		try {
			JSONArray urls = obj.getJSONArray("arrURL");
			for(int i=0; i<urls.length(); i++ ) {
				String url = urls.getString(i);
				
				// 访问一次目标网址，无视任何异常
				GetMethod accessGet = new GetMethod(url);
				try {
					accessGet.addRequestHeader("Referer", "http://video.sina.com.cn/index.shtml");
					client.executeMethod(accessGet);
				} catch (Exception e) {
				} finally {
					if(accessGet != null) accessGet.releaseConnection();
				}

			}
		} catch (Exception e) {
		}
	}
	
	/**
	 * 获取hach值
	 * @return
	 * @throws Exception
	 */
	private String getHach() throws Exception {
		GetMethod hachGet = new GetMethod(HACH_URL);
		int respCode = client.executeMethod(hachGet);
		if(respCode != HttpStatus.SC_OK)
			throw new HttpException(respCode);

		// 获取响应内容
		String body = HttpUtils.readResponseStream(hachGet);
		hachGet.releaseConnection();

		// 截取hach值
		Matcher m = Pattern.compile("hach=\\w+&").matcher(body);
		String hach = m.find() ? 
				body.substring(m.start() + 5, m.end() - 1) : null;
		return hach;
	}

	/**
	 * 从相应信息中截取vid值
	 * @param retVal
	 * @return
	 */
	private String pickVID(String retVal) {
		String vid = null;

		Pattern p = Pattern.compile("videoid=\\d+&");
		Matcher m = p.matcher(retVal);
		if(m.find()) {
			vid = retVal.substring(m.start() + 8, m.end() - 1);
		}
		return vid;
	}
}
